/*
    libhdvbd - Library for building sophisticated DVB applications
    Copyright (C) 2012 Tony Houghton <h@realh.co.uk>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <glib.h>

#include <string.h>

#include "dvbenc.h"
#include "i18n.h"

const char *
iso8859_n(int n)
{
    static char **iso8859_tab = NULL;

    if (!iso8859_tab)
        iso8859_tab = g_new0(char *, 15);
    if (!iso8859_tab[--n])
        iso8859_tab[n] = g_strdup_printf("ISO_8859-%d", n + 1);
    return iso8859_tab[n];
}

char *
hdvb_dvb_str_to_utf8(const guint8 *s, int l)
{
    const char *from_enc = NULL;
    char *decoded;
    char *tmp = NULL;

    if (!s || !l)
    {
        g_warning(_("NULL string in EPG"));
        return NULL;
    }
    if (s[0] == 0 || s[0] >= 0x20)
    {
        int n;

        from_enc = "ISO_6937-2";
        /* Need to replace Euro */
        for (n = 0; n < l; ++n)
        {
            if (s[n] == 0xa4)
            {
                if (!tmp)
                {
                    tmp = g_strndup((const char *) s, l);
                    s = (const guint8 *) tmp;
                }
                tmp[n] = 0x90;     /* Random user-defined code */
            }
        }
    }
    else if (s[0] <= 0xb)
        from_enc = iso8859_n(s[0] + 4);
    else if (s[0] == 0x10 && s[1] == 0 && s[2] > 0 && s[2] <= 0xf)
        from_enc = iso8859_n(s[2]);
    else if (s[0] == 0x11)
        from_enc = "ISO-10646";
    else if (s[0] == 0x13)
        from_enc = "GB2312";
    else if (s[0] == 0x14)
        from_enc = "BIG-5";
    else if (s[0] == 0x15)
        from_enc = "ISO-10646/UTF-8";

    if (s[0] == 0x10)
    {
        s += 3;
        l -= 3;
    }
    else if (s[0] < 0x20)
    {
        s += 1;
        l -= 1;
    }

    if (from_enc)
    {
        GError *error = NULL;

        decoded = g_convert_with_fallback((char *) s, l, "UTF-8", from_enc,
                NULL, NULL, NULL, &error);
        if (error)
        {
            g_warning(decoded ?
                    _("Partial failure decoding string which was supposedly "
                    "%s: %s") :
                    _("Total failure decoding string which was supposedly "
                    "%s: %s"),
                    from_enc, error ? error->message : _("unknown reason"));
            g_error_free(error);
        }
        if (!decoded)
            return NULL;
    }
    else
    {
        decoded = g_strndup((char *) s, l);
    }

    /* g_convert already usefully converts 0x80-0x9f to 0xc2,0x80-0x9f but we
     * need to check for DVB's Euro extension to ISO_6937
     */
    if (tmp)
    {
        int n, m;
        int dl = strlen(decoded);
        int n_euros = 0;
        char *decoded2;

        for (n = 0; n < dl; ++n)
        {
            if ((guint8) decoded[n] == 0xc2 && (guint8) decoded[n + 1] == 0x90)
                n_euros += 1;
        }
        decoded2 = g_malloc(dl + n_euros);
        for (m = n = 0; n < dl; ++n, ++m)
        {
            if ((guint8) decoded[n] == 0xc2 && (guint8) decoded[n + 1] == 0x90)
            {
                decoded2[m++] = 0xe2;
                decoded2[m++] = 0x82;
                decoded2[m] = 0xac;
                ++n;
            }
            else
            {
                decoded2[m] = decoded[n];
            }
        }
        decoded2[m] = 0;
        g_free(decoded);
        decoded = decoded2;
        g_free(tmp);
    }
    return decoded;
}
